<script setup lang="ts">
/**
 * @desc DingFlow
 * @author MiyueFE <https://github.com/miyuesc>
 * @since 2024/7/17 下午5:21
 */

import type { ComponentInstance, PropType } from 'vue'
import { computed, nextTick, onMounted, ref, shallowRef } from 'vue'
import type {
  BaseNode,
  FlowDirection,
  SubprocessNode,
} from '@/types'
import { createPresetProcess, getNodeInMap } from '@/utils/element-utils'
import FlowCanvas from '@/components/ding-flow/base/FlowCanvas.vue'
import { delegate } from '@/utils/dom-event'

defineOptions({ name: 'DingFlow' })

const $props = defineProps({
  data: {
    type: Object as PropType<BaseNode>,
    default: () => null,
  },
  direction: {
    type: String as PropType<FlowDirection>,
    default: 'vertical',
    validator: (v: FlowDirection) => ['vertical', 'horizontal'].includes(v),
  },
})
const doEmit = defineEmits([
  'update:data',
  'zoomChanged',
  'node-click',
  'node-dblclick',
  'node-mouseenter',
  'node-mouseleave',
  'node-contextmenu',
])

const root = ref<SubprocessNode>()
const computedFlowData = computed<BaseNode>(() => $props.data || ref(createPresetProcess()).value)
const computedVisibleFlowData = computed<BaseNode>({
  get: () => {
    if (root.value) {
      return root.value.$start!
    }
    return computedFlowData.value
  },
  set: () => doEmit('update:data', computedFlowData.value),
})
interface BreadcrumbItem {
  id: string
  label: string
  disabled: boolean
  node?: BaseNode
}
const computedBreadcrumbList = computed<BreadcrumbItem[] | undefined>(() => {
  if (!root.value) {
    return undefined
  }

  const list: BreadcrumbItem[] = []
  let parent: BaseNode | undefined = root.value
  while (parent) {
    const disabled: boolean = parent.id === root.value.id
    list.unshift({ node: parent, id: parent.id, label: parent.name || parent.type, disabled })
    parent = parent.$parent
  }
  list.unshift({ label: '根节点', id: '__root', disabled: false })
  return list
})

const canvas = shallowRef<ComponentInstance<typeof FlowCanvas>>()
const fitViewport = (padding?: number) => canvas.value?.fitViewport(padding)

function toggleRoot(r?: SubprocessNode) {
  if (!r || r.type === 'subprocess') {
    root.value = r
    nextTick(() => fitViewport())
  }
}
function breadcrumbClick(i: BreadcrumbItem) {
  if (i.disabled)
    return
  toggleRoot(i.node ? getNodeInMap(i.node.id) as SubprocessNode : undefined)
}

// dom 事件代理
function emitDomEvent(type: 'node-click' | 'node-dblclick' | 'node-mouseenter' | 'node-mouseleave' | 'node-contextmenu', element: HTMLElement, ev: Event) {
  const nodeId = element.getAttribute('data-node-id')
  if (nodeId) {
    const node = getNodeInMap(nodeId)
    doEmit(type, node, ev)
  }
}
function delegateDomEvents() {
  delegate(canvas.value!.$el, 'click', '.flow-node__container .flow-node', (element, ev) => {
    emitDomEvent('node-click', element, ev)
  })
  delegate(canvas.value!.$el, 'dblclick', '.flow-node__container .flow-node', (element, ev) => {
    emitDomEvent('node-dblclick', element, ev)
  })
  delegate(canvas.value!.$el, 'mouseenter', '.flow-node__container .flow-node', (element, ev) => {
    emitDomEvent('node-mouseenter', element, ev)
  })
  delegate(canvas.value!.$el, 'mouseleave', '.flow-node__container .flow-node', (element, ev) => {
    emitDomEvent('node-mouseleave', element, ev)
  })
  delegate(canvas.value!.$el, 'contextmenu', '.flow-node__container .flow-node', (element, ev) => {
    emitDomEvent('node-contextmenu', element, ev)
  })
}
onMounted(() => delegateDomEvents())

defineExpose({
  fitViewport,
  toggleRoot,
})
</script>

<template>
  <FlowCanvas ref="canvas" @zoom-changed="$emit('zoomChanged', $event)">
    <template #header>
      <div v-show="computedBreadcrumbList" class="ding-flow_breadcrumbs">
        <div
          v-for="(i, idx) in computedBreadcrumbList"
          :key="i.id"
          class="ding-flow_breadcrumb-item"
        >
          <span v-show="idx > 0" class="ding-flow_breadcrumb-item-tag">
            >
          </span>
          <span
            class="ding-flow_breadcrumb-item-label"
            :class="{
              'breadcrumb-item_is-disabled': i.disabled,
            }"
            @click="breadcrumbClick(i)"
          >{{ i.label }}</span>
        </div>
      </div>
    </template>
    <DingFlowList
      v-model:data="computedVisibleFlowData"
      :direction="direction"
    />
  </FlowCanvas>
</template>
